/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package GlobalObservingSystem.Mundo.RecepcionDatos;

import GlobalObservingSystem.Interfaces.IManejadorTrama;
import GlobalObservingSystem.Mundo.BaseDatos.AdministradorBD;
import GlobalObservingSystem.Mundo.BaseDatos.MedidaBD;
import GlobalObservingSystem.Mundo.Coordenada;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Esta clase recibe las tramas enviadas por las estasiones, las procesa para luego ser enviadas a una cola de datos.
 * @author Luis Cabarique
 */
/**
 * Esta clase recibe las tramas enviadas por las estasiones, las procesa para luego ser enviadas a una cola de datos.
 * @author Luis Cabarique
 */
public class ManejadorTrama implements IManejadorTrama {

    //==========================Atributos Estaticos de la clase=================
    private static final byte ARROBA = (byte) '@';
    private static final byte NUMERAL = (byte) '#';
    private static final byte AMPERSAN = (byte) '&';
    private static final byte BOYA = (byte) 1;
    private static final byte AVION = (byte) 2;
    private static final byte SATELITE = (byte) 3;
    private static final byte GLOBO = (byte) 4;
    private static final byte ESTACION = (byte) 5;
    private static final byte[] ESTACIONES = {BOYA, AVION, SATELITE, GLOBO, ESTACION};
    private static final int NUMERO_ESTACIONES = 5;
    private static final byte PRESION = (byte) 1;
    private static final byte VELOCIDAD = (byte) 2;
    private static final byte DIRECCION = (byte) 3;
    private static final byte TEMPERATURA_AIRE = (byte) 4;
    private static final byte HUMEDAD = (byte) 5;
    private static final byte TEMPERATURA_AGUA = (byte) 6;
    private static final byte LLUVIA = (byte) 7;
    private static final byte[] MEDICIONES = {PRESION, VELOCIDAD, DIRECCION, TEMPERATURA_AIRE, HUMEDAD, TEMPERATURA_AGUA, LLUVIA};
    private static final int NUMERO_MEDICIONES = 7;
    private static final int HEADER = 4;
    private static final int CAMPO2 = 1;
    private static final int CAMPO3 = 4;
    private static final int CAMPO4 = 4;
    private static final int CAMPO5 = 4;
    private static final int CAMPO6 = 4;
    private static final int CAMPO7 = 1;
    private static final int DATOS1 = 1;
    private static final int DATOS2 = 4;
    private static final int DATOS3 = 8;
    private static final int RELLENO = 50;
    private static final int FOOTER = 4;

    //=============================Atributos de la clase=======================
    /**
     * Atributo de la trama.
     */
    private int datosTrama;
    /**
     * Atributo de la pocision de reinicio.
     */
    private int pocisionReinicio;
    /**
     * Atributo del numero de medidas.
     */
    private int numeroMedidas;
    /**
     * Atributo del texo de encabezado
     */
    private String textoEncabezado;
    /**
     * Atributo del numero de la cola caliente.
     */
    private int colaCliente;
    /**
     * Atributo del dato.
     */
    private MedidaBD dato;

    private static final String RUTA_ARCHIVO = "./data/servidor.properties";
    private ServerSocket receptor;
    private Properties config;
    private static ManejadorTrama instancia;
    private ThreadRecibirConexiones th;
    private int puerto = 9999;

    //==========================Constructor de la clase=========================

    /**
     * Metodo que inicializa una trama.
     */
    private ManejadorTrama( )
    {
        try {
            cargarConfiguracion(RUTA_ARCHIVO);
            th = new ThreadRecibirConexiones(this);
            th.run();
        } catch (IOException ex) {
            ex.printStackTrace();
            Logger.getLogger(ManejadorTrama.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    //==========================Metodos de la clase=============================

    public static ManejadorTrama getInstance()
	{
		if( instancia == null )
			instancia = new ManejadorTrama( );
		return instancia;
	}

    public void recibirConexiones() {

		//
		// Crea otro puerto dedicado a atender otros pedidos de los clientes
		//String aux = config.getProperty("servidor.puerto");
		try {
			receptor = new ServerSocket(puerto);
			while (true) {
                            System.out.println("Esperando");
                            System.out.println(puerto);
				// Esperar una nueva conexión
				Socket socketNuevoCliente = receptor.accept();

				// Atiende al usuario
                                (new ThreadProcesarTrama(this, socketNuevoCliente)).run();
				//recibirTrama(socketNuevoCliente);
			}
		} catch (IOException e) {
			e.printStackTrace();
                        System.out.println("Se jodió");
                        puerto--;
                        recibirConexiones();
                        System.out.println("Nuevo puerto");
		} finally {
			try {
				if(receptor != null && !receptor.isClosed())
					receptor.close();
                                System.out.println("Se jodió");
			} catch (Exception e) {
				e.printStackTrace();
				System.out.println("oh no");
			}
		}
    }

    /**
     * Este metodo recive la trama para verificar su integridad y luego le extrae los datos.
     * pre: La trama es no nula y cumple el formato establecido.
     * post: Los datos son extraidos y puestos en cola para ser procesados.
     * @param trama la trama que se va a verificar y procesar.
     */
    public byte[] recibirTrama( Socket s )
    {
        BufferedReader in;
        try {
            //Abre el canal
            in = new BufferedReader(new InputStreamReader(s.getInputStream()));

            //Lee la trama
            String ss = in.readLine();

            byte[] trama = new byte[ss.length()];

            for( int i = 0; i < ss.length(); i++ )
            {
                trama[i] = (byte)ss.charAt(i);
            }
            
            return trama;

        }
        catch (IOException ex)
        {
            ex.printStackTrace();
            Logger.getLogger(ManejadorTrama.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }



    }

    /**
     * Convierte la trama en bytes a los datos que representan y los agrega a la base de datos
     * @param trama bytes que representan información de medidas
     */
    public void procesarTrama(byte[] trama)
    {

        if( trama == null )
        {
            System.out.println( "Error0" );
            return;
        }

        System.out.println( new String(trama) );
        int l = trama.length;

        if( l < 75 )
        {
            System.out.println("Error1");
            return;
        }

        if( trama[0]!=ARROBA || trama[1]!=ARROBA || trama[2]!=ARROBA || trama[3]!=ARROBA )
        {
            System.out.println("Error2");
            return;
        }

        if( trama[l-1]!=NUMERAL || trama[l-2]!=NUMERAL || trama[l-3]!=NUMERAL || trama[l-4]!=NUMERAL )
        {
            System.out.println("Error3");
            return;
        }

//        if( trama[l-5]!=AMPERSAN || trama[l-6]!=AMPERSAN || trama[l-7]!=AMPERSAN || trama[l-8]!=AMPERSAN )
//        {
//            System.out.println("Error4");
//            return;
//        }

        byte[] temp2 = new byte[2];
        byte[] temp4 = new byte[4];


        try
        {
            int tipo = trama[4];

            temp4[0] = trama[5];
            temp4[1] = trama[6];
            temp4[2] = trama[7];
            temp4[3] = trama[8];
            int id = bytearray2int(temp4);

            temp2[0] = trama[9];
            temp2[1] = trama[10];
            double latitud = bytearray2int(temp2);

            if( latitud >= 0 )
                latitud += trama[11]/60 + trama[12]/360;
            else
                latitud -= trama[11]/60 + trama[12]/360;

            temp2[0] = trama[13];
            temp2[1] = trama[14];
            double longitud = bytearray2int(temp2);

            if( latitud >= 0 )
                latitud += trama[15]/60 + trama[16]/360;
            else
                latitud -= trama[15]/60 + trama[16]/360;

            temp4[0] = trama[17];
            temp4[1] = trama[18];
            temp4[2] = trama[19];
            temp4[3] = trama[20];
            int altura = bytearray2int(temp4);

            l = trama[21];
            int c = 22;

            Coordenada lat = new Coordenada( latitud );
            Coordenada lon = new Coordenada( longitud );

            for( int i = 0; i < l; i++ )
            {
                Date d = new Date();
                int valor = 0;
                try
                {
                //Desplazamiento
                    int j = i*13;

                    int tipoMed = trama[c+j];

                    temp4[0] = trama[c+j+1];
                    temp4[1] = trama[c+j+2];
                    temp4[2] = trama[c+j+3];
                    temp4[3] = trama[c+j+4];
                    valor = bytearray2int(temp4);

                    
                    int temp = (int) trama[c+j+5];
                    d.setDate(temp);
                    temp = (int) trama[c+j+6];
                    d.setMonth(temp);
                    temp2[0] = trama[c+j+7];
                    temp2[1] = trama[c+j+8];
                    d.setYear(bytearray2int(temp2));
                    temp = (int) trama[c+j+9];
                    d.setHours(temp + (int) trama[c+j+11]*12);
                    temp = (int) trama[c+j+10];
                    d.setMinutes(temp);
                    temp = (int) trama[c+j+11];
                    d.setSeconds(temp);
                }
                catch( Exception em )
                {
                    System.out.println("Patatus");
                }

                try {
                    AdministradorBD.getInstance().ingresarMedida(latitud, longitud, (double) altura, tipo, (java.sql.Date) d, valor);
                    System.out.println("Trama ok");
                } catch (Exception ex) {
                    System.out.println("Error Catch");
                    Logger.getLogger(ManejadorTrama.class.getName()).log(Level.SEVERE, null, ex);
                }

            }
        }
        catch( Exception e )
        {
            System.out.println("Joda:"+e.getMessage());
        }


    }

    private void cargarConfiguracion(String archivo) throws IOException
    {
        //FileInputStream fis = new FileInputStream(archivo);
        config = new Properties();
        config.put("servidor.puerto", "9999");
        //config.load(fis);
        //fis.close();
    }

    private static byte[] int2bytearray(int i) {
        byte b[] = new byte[4];

        ByteBuffer buf = ByteBuffer.wrap(b);
        buf.putInt(i);
        return b;
    }

    private static int bytearray2int(byte[] b) {
        ByteBuffer buf = ByteBuffer.wrap(b);
        return buf.getInt();
    }
}
